/**
 * <p>文件名:	MemcacheManager.java</p>
 * <p>版权:		CopyrightTag</p>
 * <p>公司:		CompanyTag</p>
 * @author		周华彬(zhouhuabin@ctfo.com, zhou_hua_bin@163.com)
 */
package com.caits.lbs.framework.services.memcache;

/**
 * <p>MemcacheManager</p>
 * <p>TODO</p>
 *
 * @author		周华彬(zhouhuabin@ctfo.com, zhou_hua_bin@163.com)
 * @author 		宋晓达 ٩(·̮̃·̃)۶ (songxiaoda@ctfo.com)
 * @version		1.0.0
 * <table style="border:1px solid gray;">
 * <tr>
 * <th width="100px">版本号</th><th width="100px">动作</th><th width="100px">修改人</th><th width="100px">修改时间</th>
 * </tr>
 * <!-- 以 Table 方式书写修改历史 -->
 * <tr>
 * <td>0.0.0</td><td>创建类</td><td>zhb</td><td>2010-11-30 下午03:56:48</td>
 * </tr>
 * <tr>
 * <td>1.0.0</td><td>类改造, 可通过外部对服务器地址进行配置</td><td>sxd</td><td>2011-01-06 11:43</td>
 * </tr>
 * </table>
 */
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeoutException;

import net.rubyeye.xmemcached.exception.MemcachedException;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.log4j.Logger;
import org.springframework.util.Assert;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.util.SafeEncoder;

import com.caits.lbs.exception.LBSException;
import com.caits.lbs.framework.log.CommonLogFactory;
import com.caits.lbs.framework.utils.ObjectSizeUtils;
import com.caits.lbs.framework.utils.StringUtils;

/**
 * <p>
 * MemcacheManager
 * </p>
 * <p>
 * 所有需要调用memcache的地方都可以取得该类的实例，设置好serverAddress后就可以使用
 * </p>
 * 
 * @author 周华彬(zhouhuabin@ctfo.com, zhou_hua_bin@163.com)
 * @version 0.0.0
 *          <table style="border:1px solid gray;">
 *          <tr>
 *          <th width="100px">版本号</th>
 *          <th width="100px">动作</th>
 *          <th width="100px">修改人</th>
 *          <th width="100px">修改时间</th>
 *          </tr>
 *          <!-- 以 Table 方式书写修改历史 -->
 *          <tr>
 *          <td>0.0.0</td>
 *          <td>创建类</td>
 *          <td>zhb</td>
 *          <td>2011-3-18 下午05:16:27</td>
 *          </tr>
 *          <tr>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          </tr>
 *          </table>
 */
public class RedisCacheManager implements IMemcacheService {

	/** 日志记录 */
	protected static Logger log = CommonLogFactory.getLog();

	/** 采用单例模式 */
	protected static RedisCacheManager memCachedManager = null;

	/** 创建全局连接池唯一实例 */
	private JedisCluster jedisCluster;

	/** 高速缓存服务地址 */
	private String serverAddress = "";

	/** 服务器验证密码 */
	private String password;
	/** 网络超时时间阈值ms */
	private int soTimeout = 3 * 1000;

	/**
	 * 保护型构造方法
	 */
	protected RedisCacheManager() {
		if (memCachedManager != null) {
			throw new IllegalStateException("实例已经初始化，本次构造放弃.");
		}
		memCachedManager = this;
	}

	/**
	 * 初始化方法, 在applicationContext-redis.xml 中 <code>
	 * <![CDATA[
	 * <bean id="redisCacheServer" class="com.caits.lbs.framework.services.memcache.RedisCacheManager"
	 * init-method="init"\> 
	 * <property name="config" ref="jedisPoolConfig"><\/property>
	 * <property name="serverAddress" value="192.168.2.186:6379,192.168.4.115:6379"><\/property> 
	 * <\/bean>
	 * ]]>
	 * </code>
	 * */
	public void init() {
		if (jedisCluster != null) {
			throw new IllegalStateException("实例连接jedisCluster已经初始化，放弃本次执行.");
		}
		/** 设置与缓存服务器的连接 ,启用主备模式 */
		try {
			// edis Cluster will attempt to discover cluster nodes automatically
			Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
			String[] serverArr = StringUtils.splitToArray(serverAddress, ",", true);
			Assert.isTrue(serverArr.length > 0, "方法init的serverAddress参数错误");
			for (String server : serverArr) {
				String[] hostPort = StringUtils.splitToArray(server, ":", true);
				if (hostPort.length > 1) {
					jedisClusterNodes.add(new HostAndPort(hostPort[0], Integer.valueOf(hostPort[1])));
				}
			}
			if(StringUtils.notNullOrBlank(getPassword())){
				jedisCluster = new JedisCluster(jedisClusterNodes, soTimeout,5,new GenericObjectPoolConfig());
			}else{
				jedisCluster = new JedisCluster(jedisClusterNodes, soTimeout,new GenericObjectPoolConfig());
			}
			log.info("RedisCacheManager initialization successful!" + serverAddress);
		} catch (Exception e) {
			log.error("初始化RedisCacheManager连接失败:" + e.getLocalizedMessage(), e);
		}
	}

	/**
	 * 添加
	 * */
	public boolean add(String key, int exp, String value) throws LBSException {
		return put(key, exp, value);
	}

	public boolean add(String key, int exp, String value, long expiry) throws LBSException {
		return put(key, exp, value, expiry);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.caits.lbs.framework.services.memcache.IMemcacheService#put(java.lang
	 * .String, int, java.lang.Object)
	 */
	public boolean put(final String key, int exp, final Object value) throws LBSException {
		if (key == null || value == null) {
			throw new LBSException("方法put传入的对象为空");
		}
		// ShardedJedis jedis =null;
		try {
			byte[] bytes = ObjectSizeUtils.serialize(value);
			String result=null;
			if(exp>0)
				result = jedisCluster.setex(SafeEncoder.encode(key), exp, bytes);
			else
				result = jedisCluster.set(SafeEncoder.encode(key), bytes);
			log.debug("set key"+key+" result="+result);
			return true;
		} catch (Exception e) {
			String message = "设置Redis缓存异常,key=" + key;
			log.error(message, e);
			throw new LBSException(message, e);

		} finally {
			// 释放对象池

		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.caits.lbs.framework.services.memcache.IMemcacheService#put(java.lang
	 * .String, int, java.lang.Object, long)
	 */
	public boolean put(String key, int exp, Object value, long expiry) throws LBSException {
		return put(key, exp, value);
	}

	/**
	 * 根据指定的关键字获取对象. 返回值的类型是对象，不是简单字符串，须经过反序列化
	 * 
	 * @param key
	 * @return
	 * @throws MemcachedException
	 * @throws InterruptedException
	 * @throws TimeoutException
	 */
	public Object get(String key) throws LBSException {
		if (key == null) {
			throw new LBSException("方法get传入的对象为空");
		}
		try {
			byte[] bytes = jedisCluster.get(SafeEncoder.encode(key));
			Object result =ObjectSizeUtils.unserialize(bytes);
			return result;
		} catch (Exception e) {
			String message = "获取Redis缓存对象异常,key=" + key;
			log.error(message, e);
			throw new LBSException(message, e);
		} finally {
			// 释放对象池
		}

	}

	@Override
	public boolean put(byte[] key, int exp, byte[] value, long expiry) throws LBSException {
		if (key == null || value == null) {
			throw new LBSException("方法put传入的对象为空");
		}
		// ShardedJedis jedis =null;
		try {
			String result=null;
			if(exp>0)
				result = jedisCluster.setex(key, exp, value);
			else
				result = jedisCluster.set(key, value);
			log.debug("set key"+key+" result="+result);
			return true;
		} catch (Exception e) {
			String message = "设置Redis缓存异常,key=" + key;
			log.error(message, e);
			throw new LBSException(message, e);

		} finally {
			// 释放对象池

		}

	}

	@Override
	public byte[] get(byte[] key) throws LBSException {
		if (key == null) {
			throw new LBSException("方法get传入的对象为空");
		}
		try {
			byte[] bytes = jedisCluster.get(key);
			return bytes;
		} catch (Exception e) {
			String message = "获取Redis缓存对象异常,key=" + key;
			log.error(message, e);
			throw new LBSException(message, e);
		} finally {
			// 释放对象池
		}

	}

	/**
	 * 获取变量<code>soTimeout</code>的值
	 * 
	 * @return 返回的数据类型是<code>int</code>
	 */
	public int getSoTimeout() {
		return soTimeout;
	}

	/**
	 * 设置变量<code> soTimeout</code> 的值
	 * 
	 * @param soTimeout
	 *            <code>soTimeout</code> 参数类型是<code>int</code>
	 */
	public void setSoTimeout(int soTimeout) {
		this.soTimeout = soTimeout;
	}

	public String getServerAddress() {
		return serverAddress;
	}

	public void setServerAddress(String serverAddress) {
		this.serverAddress = serverAddress;
	}

	/**
	 * 获取变量<code>password</code>的值
	 * 
	 * @return 返回的数据类型是<code>String</code>
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * 设置变量<code> password</code> 的值
	 * 
	 * @param password
	 *            <code>password</code> 参数类型是<code>String</code>
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.caits.lbs.framework.services.memcache.IMemcacheService#sendMessage
	 * (java.lang.String, java.lang.String)
	 */
	@Override
	public void sendMessage(String head, String body) {
		try {
			Assert.notNull(jedisCluster, "消息发送对象jedisCluster不能为空.");
			Assert.notNull(body, "消息内容msg不能为空.");
			 jedisCluster.publish(head, body);
			log.info("消息发送成功,head:" + head + ",body:" + body);
		} catch (Exception e) {
			log.error("消息发送失败,head:" + head + ",body:" + body + ",msg=" + e.getLocalizedMessage(), e);
		} finally {

		}
	}

	/**
	 * 获取集群模式的连接对象
	 * 
	 * @return
	 */
	public static synchronized JedisCluster getCLusterConnection() {
		Assert.notNull(memCachedManager, "jedisCluster连接对象不能为空");
		return memCachedManager.jedisCluster;
	}

	/**
	 * 直接获取redis的连接实例.
	 * 
	 * @return
	 */
	public static synchronized JedisCluster getConnection() {
		return getCLusterConnection();
	}

	/**
	 * 获取本对象实例
	 * 
	 * @return
	 */
	public static RedisCacheManager getInstance() {
		return memCachedManager;
	}
}
